/**
 * Copyright (c) Daniel Centore
 *
 * ex: set filetype=java expandtab tabstop=4 shiftwidth=4 :
 *
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * This code is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation.
 */
package ronp.anim.data.filmstrip;

import java.awt.Point;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;

import ronp.anim.data.Grip;
import ronp.anim.data.objects.RNObject;
import ronp.anim.kludges.RepaintListener;
import ronp.data.BoundListModel;

/**
 * Represents a single animation (from start to finish). This is the highest level.
 * 
 * @author Daniel Centore
 *
 */
public class Animation implements Serializable
{
	private static final long serialVersionUID = 2L;

	// NOTE: CHANGES TO ANIMATION NEED TO BE REFLECTED IN THE applyUndo METHOD! //

	private transient List<RepaintListener> listeners; // Listeners for changes that require repaint

	private List<Layer> layers; // A list of layers in the animation. Each layer controls a single grip.
	private BoundListModel<RNObject> objects; // List of objects in the animation. These objects change based on their grips.
	private int height; // Height and width of the animation
	private int width;
	private int fps; // Frames-per-second to run the animation

	/**
	 * Creates an {@link Animation}
	 * @param width Height and width of the animation
	 * @param height
	 */
	public Animation(int width, int height, int fps)
	{
		this.fps = fps;
		listeners = new ArrayList<RepaintListener>();
		layers = new ArrayList<Layer>();
		objects = new BoundListModel<RNObject>();

		this.height = height;
		this.width = width;
	}

	public void applyUndo(Animation undo)
	{
		layers = undo.layers;

		// we don't just write this so it can stay linked to the JList
		objects.clear();
		objects.addAll(undo.objects);

		height = undo.height;
		width = undo.width;
		fps = undo.fps;
	}

	/**
	 * @return A list of layers, each controlling a grip
	 */
	public List<Layer> getLayers()
	{
		return layers;
	}

	/**
	 * Adds a layer to our list
	 * @param l Layer to add
	 */
	public void addLayer(Layer l)
	{
		layers.add(l);
		repaint();
	}

	/**
	 * Removes a layer from our list
	 * @param l Layer to remove
	 */
	public void removeLayer(Layer l)
	{
		layers.remove(l);
		repaint();
	}

	/**
	 * Removes a layer from our list
	 * @param index Index of the layer to remove
	 */
	public void removeLayer(int index)
	{
		layers.remove(index);
		repaint();
	}

	/**
	 * @return A list of current {@link RNObject}s in the game. These objects change based on their grips.
	 */
	public BoundListModel<RNObject> getObjects()
	{
		return objects;
	}

	/**
	 * Adds an {@link RNObject} to the animation
	 * @param obj {@link RNObject} to add
	 */
	public void addObject(RNObject obj)
	{
		objects.add(obj);
		repaint();
	}

	/**
	 * Removes an {@link RNObject} from the animation
	 * @param obj Object to remove
	 */
	public void removeObject(RNObject obj)
	{
		objects.remove(obj);
		repaint();
	}

	@Override
	public String toString()
	{
		return "Animation [layers=" + layers + "]";
	}

	/**
	 * @return The fixed height of the animation
	 */
	public int getHeight()
	{
		return height;
	}

	/**
	 * Sets the height of the animation
	 * @param height The height to set it to
	 */
	public void setHeight(int height)
	{
		this.height = height;
		repaint();
	}

	/**
	 * @return The width of the animation
	 */
	public int getWidth()
	{
		return width;
	}

	/**
	 * Sets the width of the animation
	 * @param width What to set it to
	 */
	public void setWidth(int width)
	{
		this.width = width;
		repaint();
	}

	/**
	 * Repaints the Gui
	 */
	public void repaint()
	{
		if (listeners == null)
			return;

		for (RepaintListener l : listeners)
			l.repaint();
	}

	/**
	 * Adds a listener for changes that require a repaint
	 * @param listener The {@link RepaintListener} to add
	 */
	public void addListener(RepaintListener listener)
	{
		if (listeners == null) // happens if we load the file
			listeners = new ArrayList<RepaintListener>();

		listeners.add(listener);
	}

	/**
	 * Removes a {@link RepaintListener}
	 * @param listener {@link RepaintListener} to remove
	 */
	public void removeListener(RepaintListener listener)
	{
		listeners.remove(listener);
	}

	/**
	 * Counts the number of frames (so do not call repeatedly)
	 * @return The number of frames there are atm
	 */
	public int countFrames()
	{
		int highest = 0;

		for (Layer l : layers)
		{
			int i = -1;
			Grip g = l.getGrip();
			Iterator<Entry<Integer, Point>> k = g.entrySet().iterator();
			while (k.hasNext())
			{
				int key = k.next().getKey();
				if (key > i)
					i = key;
			}

			if (i + 1 > highest)
				highest = i + 1;
		}

		return highest;
	}

	/**
	 * @return The frames-per-second this animation should run at
	 */
	public int getFps()
	{
		return fps;
	}

	/**
	 * Sets the frames-per-second
	 * @param fps FPS to set it to
	 */
	public void setFps(int fps)
	{
		this.fps = fps;
	}

}
